package com.lookme.lmtool.cracker.spliter;


import com.lookme.lmtool.cracker.annotation.ParamAnnotation;
import com.lookme.lmtool.cracker.annotation.SpliterAnnotation;
import com.lookme.lmtool.model.Rect;
import com.lookme.lmtool.model.SpliterInfo;
import com.lookme.lmtool.utils.ImageUtils;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import lombok.Getter;
import lombok.Setter;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
@Setter
@Getter
@SpliterAnnotation("滴水算法")
public class WaterDropSpliter  extends AbstractSpliter {

	@XStreamAsAttribute
	@ParamAnnotation(name = "最小字符宽度", desc = "最小字符宽度",inputType = ParamAnnotation.InputType.Spinner,min=1)
	int minLength=3;
	@XStreamAsAttribute
	@ParamAnnotation(name = "最大字符宽度", desc = "最大字符宽度",inputType = ParamAnnotation.InputType.Spinner,min=1)
	int maxLength=16;
	@XStreamAsAttribute
	@ParamAnnotation(name = "平均字符宽度", desc = "平均字符宽度",inputType = ParamAnnotation.InputType.Spinner,min=1)
	int averageLength=12;
	@XStreamAsAttribute
	@ParamAnnotation(name = "大水滴的宽度", desc = "大水滴的宽度",inputType = ParamAnnotation.InputType.Spinner,min=1)
	int waterDropLength=1;

	@XStreamAsAttribute
	@ParamAnnotation(name = "去空白边", desc = "去空白边",inputType = ParamAnnotation.InputType.Radio,width = 45)
	boolean removeBlank=false;

	@Override
	public List<SpliterInfo> split(BufferedImage src) {

		List<SpliterInfo> list=new ArrayList<>();
		int width = src.getWidth();
		int height = src.getHeight();
		if (width <= maxLength) {
			//如果是单个字符，则直接返回
			list.add(SpliterInfo.get(src, Rect.builder().x(0).y(0).width(width).height(height).build()));
			return list;
		}

		//在x轴的投影
		int[] histData = new int[width];
		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				if (isBlack(src.getRGB(x, y))) {
					histData[x]++;
				}
			}
		}

		ArrayList<Integer> extrems = getMinExtrem(histData);

		Point[] startRoute = new Point[height];
		Point[] endRoute = null;

		for(int y=0; y < height; y++){
			startRoute[y] = new Point(0, y);
		}

		int num = (int)Math.round((double)width/ averageLength);//字符的个数
		int lastP = 0; //上一次分割的位置
		int curSplit = 1;//分割点的个数，小于等于 num - 1;
		for (int i = 0; i < extrems.size(); i++) {
			if (curSplit > num - 1) {
				break;
			}

			//判断两个分割点之间的距离是否合法
			int curP = extrems.get(i);
			int dBetween = curP - lastP + 1;
			if (dBetween < minLength || dBetween > maxLength) {
				continue;
			}

//			//判断当前分割点与末尾结束点的位置是否合法
//			int dAll = width - curP + 1;
//			if (dAll < minD*(num - curSplit) || dAll > maxD*(num - curSplit)) {
//				continue;
//			}
			endRoute = getEndRoute(src,new Point(curP, 0), height, curSplit);
			doSplit(list,src,startRoute, endRoute);
			startRoute = endRoute;
			lastP = curP;
			curSplit ++;
			System.out.println(curP);
		}

		endRoute = new Point[height];
		for(int y=0; y < height; y++){
			endRoute[y] = new Point(width - 1, y);
		}
		doSplit(list,src,startRoute, endRoute);

		System.out.println("=================");
		System.out.println(width+","+height);

		return list;
	}

	public static ArrayList<Integer> getMinExtrem(int[] data){

		int min = data[0];
		ArrayList<Integer> list = new ArrayList<Integer>();

		int miniSpan = 5; //规定最小跨度
		int lastEqIndex = 0;

		for(int i=1; i<data.length - 1; i++){
			if (data[i] > data[i+1]) {
				min = data[i+1];
			}

			// 保存101的这种类型的极值
			if (data[i] < data[i+1] && data[i] <= min) {
				//判断两个极小值之间的距离是否满足最小跨度
				if ((list.size() != 0 && (i - list.get(list.size() - 1) >= miniSpan)) || (list.size() == 0)) {
					list.add(i);
				}
			}

			// 保存100000001这种类型的极值，只取第一个0，和最后一个0为极值点
			if (data[i] == data[i+1]) {
				if (lastEqIndex + 1 != i) {
					if ((list.size() != 0 && (i - list.get(list.size() - 1) >= miniSpan)) || (list.size() == 0)) {
						list.add(i);
					}
				}
				lastEqIndex = i;
			}
		}

		for (Integer integer : list) {
			System.out.print(integer+" ");
		}

		return list;
	}
	
	/**
	 * 获得滴水的路径
	 * @param startP
	 * @param height
	 * @return
	 */
	private Point[] getEndRoute(BufferedImage src,Point startP, int height, int curSplit){

		//获得分割的路径
		Point[] endRoute = new Point[height];
		Point curP = new Point(startP.x, startP.y);
		Point lastP = curP;
		endRoute[0] = curP;
		while(curP.y < height - 1){
			int maxW = 0;
			int sum = 0;
			int nextX = curP.x;
			int nextY = curP.y;
			
			for (int j = 1; j <= 5; j++) {
				try {
					int curW = getPixelValue(src,curP.x, curP.y, j) * (6 - j);
					sum += curW;
					if (curW > maxW) {
						maxW = curW;
					}
				} catch (Exception e) {
					e.printStackTrace();
					System.exit(0);
				}
			}

			//如果全黑，需要看惯性
			if (sum == 0 ) {
				maxW = 4;
			}
			//如果周围全白，则默认垂直下落
			if (sum == 15) {
				maxW = 6;
			}
			
			switch (maxW) {
			case 1: 
				nextX = curP.x - 1;
				nextY = curP.y;
				break;
			case 2:
				nextX = curP.x + 1;
				nextY = curP.y;
				break;
			case 3:
				nextX = curP.x + 1;
				nextY = curP.y + 1;
				break;
			case 5:
				nextX = curP.x - 1;
				nextY = curP.y + 1;
				break;
			case 6:
				nextX = curP.x;
				nextY = curP.y + 1;
				break;
			case 4:
				if (nextX > curP.x) {//具有向右的惯性
					nextX = curP.x + 1;
					nextY = curP.y + 1;
				}
				
				if (nextX < curP.x) {//向左的惯性或者sum = 0
					nextX = curP.x;
					nextY = curP.y + 1;
				}
				
				if (sum == 0) {
					nextX = curP.x;
					nextY = curP.y + 1;
				}
				break;

			default:
				
				break;
			}
			
			//如果出现重复运动
			if (lastP.x == nextX && lastP.y == nextY) {
				if (nextX < curP.x) {//向左重复
					maxW = 5;
					nextX = curP.x + 1;
					nextY = curP.y + 1;
				}else{//向右重复
					maxW = 3;
					nextX = curP.x - 1;
					nextY = curP.y + 1;
				}
			}
			
			lastP = curP;
			int rightLimit = averageLength *curSplit + 1;
			if (nextX > rightLimit) {
				nextX = rightLimit;
				nextY = curP.y + 1;
			}
			
			int leftLimit = averageLength *(curSplit - 1) + averageLength /2;
			if (nextX < leftLimit) {
				nextX = leftLimit;
				nextY = curP.y + 1;
			}
			curP = new Point(nextX, nextY);
			
			endRoute[curP.y] = curP;
		}
	
		return endRoute;
	}
	
	/**
	 * 具体实行切割
	 * @param starts
	 * @param ends
	 */
	private void doSplit(List<SpliterInfo> list,BufferedImage src,Point[] starts, Point[] ends){
		int left = starts[0].x;
		int top = starts[0].y;
		int right = ends[0].x;
		int bottom = ends[0].y;
		
		for (int i = 0; i < starts.length; i++) {
			left = Math.min(starts[i].x, left);
			top = Math.min(starts[i].y, top);
			right = Math.max(ends[i].x, right);
			bottom = Math.max(ends[i].y, bottom);
		}
		
		int width = right - left + 1;
		int height = bottom - top + 1;
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
		
		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				image.setRGB(x, y, new Color(255, 255, 255).getRGB());
			}
		}
		for (int i = 0; i < ends.length; i++) {
			Point start = starts[i];
			Point end = ends[i];
			for (int x = start.x; x < end.x; x++) {
				if (isBlack(src.getRGB(x, i))) {
//					System.out.println((x - left) + ", " + (start.y - top));
					image.setRGB(x - left, start.y - top, new Color(0, 0, 0).getRGB());
				}
			}
			
		}
		Rect rect=Rect.builder().x(left).y(top).width(width).height(height).build();
		if(removeBlank) {
			rect = ImageUtils.getRemoveBlankRect(src, rect);
		}
		SpliterInfo spliterInfo=SpliterInfo.builder().image(image).rect(rect).build();
		list.add(spliterInfo);
		
		System.out.println("-----------------------");
		
	}
	
	
	/**
	 * 判断是否位黑色像素
	 * @param rgb
	 * @return
	 */
	private boolean isBlack(int rgb) {
		Color color = new Color(rgb);
		if (color.getRed() + color.getGreen() + color.getBlue() <= 300) {
			return true;
		}
		return false;
	}
	
	/**
	 * 获得大水滴中心点周围的像素值
	 * @param cx
	 * @param cy
	 * @param j 中心点周围的编号
	 * @return
	 */
	private int getPixelValue(BufferedImage src,int cx, int cy, int j){
		int rgb = 0;
		
		if (j == 4) {
			int right = cx + waterDropLength + 1;
			right = right >= src.getWidth() - 1 ? src.getWidth() - 1: right;
			rgb = src.getRGB(right, cy);
			return isBlack(rgb) ? 0 : 1;
		}
		
		if (j == 5) {
			int left = cx - waterDropLength - 1;
			left = left <=0 ? 0 : left;
			rgb = src.getRGB(left, cy);
			return isBlack(rgb) ? 0 : 1;
		}
		
		//如果 1<= j <= 3, 则判断下方的区域， 只要有一个黑点，则当做黑点，
		int start = cx - waterDropLength + j - 2;
		int end = cx + waterDropLength + j - 2;
		
		start = start <=0 ? 0 : start;
		end = end >= src.getWidth() - 1 ? src.getWidth() - 1 : end;
		int blackNum = 0;
		int whiteNum = 0;
		for (int i = start; i <= end; i++) {
			rgb = src.getRGB(i, cy + 1);
			if (isBlack(rgb)) {
				blackNum ++;
			}else {
				whiteNum ++;
			}
		}
		
		return (blackNum >= whiteNum) ? 0 : 1;
	}


}
